Repository navigation
Prevent double fetch. - #67
Conversation
When using <React.StrictMode> the useEffect fires twice which causes the token to be loaded twice. Because the server only allows the token to be loaded once we have to ensure that if we've already fetched the token we don't do it again.
There was a problem hiding this comment.
Pull request overview
This PR prevents double fetching of LTI tokens when using React StrictMode by implementing a ref-based guard in the useEffect hook. In development mode with StrictMode enabled, useEffect runs twice, which was causing the token fetch to fail on the second attempt since the server only allows a token to be retrieved once.
Changes:
- Added
hasFetchedRefusinguseRefto track whether token has been fetched - Added guard in useEffect to prevent multiple fetches
- Added test case to verify behavior in StrictMode
- Minor formatting updates (trailing commas)
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/components/tokenRetriever/LtiTokenRetriever.tsx | Implements ref-based guard to prevent duplicate token fetches in StrictMode |
| src/components/tokenRetriever/LtiTokenRetriever.test.jsx | Adds test to verify only one fetch occurs in StrictMode |
Comments suppressed due to low confidence (1)
src/components/tokenRetriever/LtiTokenRetriever.tsx:95
- The useEffect has
ltiServerin its dependency array, but thehasFetchedRef.currentguard prevents the effect from running more than once. This creates an inconsistency: ifltiServerchanges after the initial mount, the effect will be triggered but won't actually execute the fetch due to the ref check.
Since this component is designed to fetch a one-time token only once (as mentioned in the comments), the dependency array should be empty. Change [ltiServer] to [] to accurately reflect that this effect should only run on mount, regardless of prop changes.
useEffect(() => {
if (hasFetchedRef.current) return;
hasFetchedRef.current = true;
const fetchToken = async () => {
const token = getToken();
const server = getServer();
if (!token) {
setState({ loading: false, error: "No id found to load token with" });
return;
}
if (!server) {
setState({ loading: false, error: "No server found to load from" });
return;
}
try {
const formData = new FormData();
formData.append('key', token);
const response = await fetch(`${server}/token`, {
method: 'POST',
body: formData,
});
if (!response.ok) {
if (response.status === 403) {
throw new Error("Sorry the tool is not currently available to you.");
}
// Try to get cached JWT
const cachedJwt = loadJwt();
if (!cachedJwt) {
throw new Error("Failed to load token.");
}
handleJwt(cachedJwt, server);
setState({ loading: false, error: null });
return;
}
const json = await response.json();
const jwt = json.jwt || json.token_value;
if (!jwt) {
throw new Error("Failed to load token.");
}
handleJwt(jwt, server);
saveJwt(jwt);
setState({ loading: false, error: null });
} catch (error) {
const message = error instanceof Error ? error.message : "Failed to load token.";
setState({ loading: false, error: message });
}
};
fetchToken();
}, [ltiServer]);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|



When using <React.StrictMode> the useEffect fires twice which causes the token to be loaded twice. Because the server only allows the token to be loaded once we have to ensure that if we've already fetched the token we don't do it again.